<?
/** The heart of Dataxi. Implements a state machine that controls dataxi.
 *
 * This collection of functions (libary) is licensed under GPL2.
 * See LICENSE or www.gnu.org for more details.
 *
 * Jarva call this when it receives an request that is directed to dataxi.
 * This is also the entry point to Asema.
 *
 * Asema is the part between Kone and user.It is considered with user interface
 * and client events. It sends requests to kone to get the required information
 * from the db.
 *
 * $Id: dzmanager.php 595 2006-04-03 16:00:47Z jyry $
 *
 */


include_once("sodebug.php");
include_once("sosqlhi.php");
include_once("soset.php");
include_once("sostreams.php");

include_once("dzconst.php");
include_once("dzsmconst.php");
include_once("dzsession.php");
include_once("dzevent.php");
include_once("dzstates.php");
include_once("dzgeneric.php");
include_once("dzerror.php");


/* Define flag for data serialisation */
define(DZMNG_SER_YES, 1); /* Serialise data */
define(DZMNG_SER_NO, 2); /* Don't serialise data */
define(DZMNG_SER_DEL, 3); /* Remove old serialised data */


/** Dataxi manager that takes care of sessions.
 *  >VarSet:soSet  Variables that came in from the client side.
 *  <:dzManagerResult The end status of the state machine and next screen.
 */
function dzManage($VarSet) {
    $session_id = $VarSet->getItem(DZRI_SESSION);

    /* Mark session as under processing. */
    $under_process_result = addToProcessedList($session_id);

    /* Create the data structure that contains the session data  */
    /* Note that password is in the variable set only when logging in.
       Later it can be found in dzSession. There is no use to send
       password back and forth after succesful login. */
    $SesData = dzManRestoreSession($session_id);

    /* No serialized session data found so create a new session. */
    if ($SesData == NULL) {
        $SesData = dzManNewSession($session_id,
                                   $VarSet->getItem(DZRI_USER),
                                   $VarSet->getItem(DZRI_PASSWD),
                                   $VarSet->getItem(DZRI_LANGUAGE));
        /* Preserve the current state when entering the state machine */
    } else if ($VarSet->GetItem(DZRI_STARTING)) {
        /* Returned to an old session. We must find out if the session crashed
           last time to avoid crashing again. */
        // XXX crash handling
        soDebug("dzManager: Restarting a session.", SOD_DET);
        $SesData->State = DZ_STATE_PRODUCE_OUTPUT;
    } else {
        soDebug("dzManager: Restoring a session ", SOD_DET);
        /* Check if the session was hijaaked */
        if ($VarSet->getItem(DZRI_LASTACCESS) != $SesData->LastAccess) {
            soDebug("dzManager: The session was hijaaked ", SOD_DET);

            /* The session was hijaaked to other place. Terminate this session.
               The user must relogin to hijaak the session again. */
            $msg = "This session was hijaaked. You should relogin to hijaak ".
                   "the session back or start a new session.";
            /* Use system error functionality. Altough this is not a system error.
               This way the session can be easily terminated. */
            dzSysError($msg, DZSE_NOTIFY, true);
        } else {
            $SesData->State = DZ_STATE_INITIAL;
        } // if $VarSet->getItem(DZRI_LASTACCESS)...
    } // if $SesData == NULL

    /* Data from the widgets on the form. These was posted by the client */
    $SesData->HotView->Template->WidgetData = $VarSet;

    /* Construct a giant debug message */
    $tmp = "dzUser=".$VarSet->getItem(DZRI_USER);
    $tmp .= " dzSession=".$VarSet->getItem(DZRI_SESSION);
    $tmp .= " dzWidget=".$VarSet->getItem(DZRI_WIDGET);
    $tmp .= " dzFrame=".$VarSet->getItem(DZRI_FRAME);
    $tmp .= " dzEvent=".$VarSet->getItem(DZRI_EVENT);
    $tmp .= " dzOptions=".$VarSet->getItem(DZRI_OPTIONS);
    $tmp .= " State=".$SesData->State;
    soDebug("dzManager: $tmp", SOD_DET);

    /* Run the state machine */
    return dzRunStateMachine($SesData);
}

/** function dzRunStateMachine($SesData)
 * The State machine that controls Dataz processing for a session.
 * File dzstates.php contains handlers for the different states.
 * Write your handlers there and call them from here. The purpose is to
 * keep the state machine so compact as possible.
 * Params:
 * $SesData the session information
 */
function dzRunStateMachine($SesData) {
    /* Set the access time for the session. Access time is send together with
       the form to client. Later access times are compared when the user
       send next request to Dataxi. If timestamps doesn't match the session
       has probably been hijaaked to some place else meanwhile.

       If "protect_LastAccess" session flag is set the access time is left
       unaltered. This is when we do not know if the user is going to hijaak the
       session back. */
    if (!$SesData->querySesFlag("protect_LastAccess")) {
        $SesData->LastAccess = time();
    }; // if
    /* Enter the state machine */
    for (;;) {
        switch ($SesData->State) {
            case DZ_STATE_ACTIVATE_VIEW:
                soDebug("dzManager: DZ_STATE_ACTIVATE_VIEW", SOD_LO);
                $SesData->State = dzSMH_ActivateView($SesData);
                break;

            case DZ_STATE_ADD_LINE:
                soDebug("dzManager: DZ_STATE_ADD_LINE", SOD_LO);
                $SesData->State = dzSMH_AddLine($SesData);
                break;

            case DZ_STATE_ADD_VIEW:
                soDebug("dzManager: DZ_STATE_ADD_VIEW", SOD_LO);
                $SesData->State = dzSMH_AddView($SesData);
                break;

            case DZ_STATE_BROWSE_NEXT:
                soDebug("dzManager: DZ_STATE_BROWSE_NEXT", SOD_LO);
                $SesData->State = dzSMH_BrowseJump($SesData, 1);
                break;

            case DZ_STATE_BROWSE_PREVIOUS:
                soDebug("dzManager: DZ_STATE_BROWSE_PREVIOUS", SOD_LO);
                $SesData->State = dzSMH_BrowseJump($SesData, -1);
                break;

            case DZ_STATE_CANCEL_EDIT:
                soDebug("dzManager: DZ_STATE_CANCEL_EDIT", SOD_LO);
                $SesData->State = dzSMH_CancelEdit($SesData);
                break;

            case DZ_STATE_CHANGE_BROWSE_MODE:
                soDebug("dzManager: DZ_STATE_CHANGE_BROWSE_MODE", SOD_LO);
                $SesData->State = dzSMH_ChangeBrowseMode($SesData);
                break;

            case DZ_STATE_CHANGE_PAGE:
                soDebug("dzManager: DZ_STATE_CHANGE_PAGE", SOD_LO);
                $options = $SesData->HotView->Template->WidgetData->getItem(DZRI_OPTIONS);
                $SesData->HotView->Template->PageNo = $options;
                $SesData->State = DZ_STATE_RECOVER_ERROR;
                break;


            case DZ_STATE_CHANGE_VIEW:
                soDebug("dzManager: DZ_STATE_CHANGE_VIEW", SOD_LO);
                $options = $SesData->HotView->Template->WidgetData->getItem(DZRI_OPTIONS);
                soDebug("dzManager: Change to view ".$options, SOD_DET);
                $id = intval($options);
                $pos = $SesData->ViewOrder->findItem($id);

                /* Move the chosen view as first in the view list */
                $SesData->ViewOrder->moveItem($pos, 0);
                $SesData->ViewPos = 0;
                soDebug("dzManager: At position ".$pos, SOD_DET);
                $SesData->State = DZ_STATE_ACTIVATE_VIEW;
                break;

            case DZ_STATE_CHANGE_ORDER:
                soDebug("dzManager: DZ_STATE_CHANGE_ORDER", SOD_LO);
                $SesData->State = dzSMH_ChangeOrder($SesData);
                break;

            case DZ_STATE_COMMIT_EDIT:
                soDebug("dzManager: DZ_STATE_COMMIT_EDIT", SOD_LO);
                $SesData->State = dzSMH_CommitEdit($SesData);
                break;

            case DZ_STATE_COMMIT_PSEUDO_FORM:
                soDebug("dzManager: DZ_STATE_COMMIT_PSEUDO_FORM", SOD_LO);
                $SesData->State = dzSMH_CommitPseudoForm($SesData);
                break;

            case DZ_STATE_DEL_LINE:
                soDebug("dzManager: DZ_STATE_DEL_LINE", SOD_LO);
                $SesData->State = dzSMH_DelLine($SesData);
                break;

            case DZ_STATE_DEL_ROW:
                soDebug("dzManager: DZ_STATE_DEL_ROW", SOD_LO);
                $SesData->State = dzSMH_DelRow($SesData);
                break;

            case DZ_STATE_FINISH:
                soDebug("dzManager: DZ_STATE_FINISH", SOD_LO);
                dzSMH_Finish($SesData);
                return $SesData->HotView->Template->RequestResult;  // <-- EXIT
                break;

            case DZ_STATE_FINISH_PICK_UP:
                soDebug("dzManager: DZ_STATE_FINISH_PICK_UP", SOD_LO);
                $SesData->State = dzSMH_FinishPickUp($SesData);
                break;

            case DZ_STATE_FROM_BROWSE:
            case DZ_STATE_FROM_LOOK_UP:
            case DZ_STATE_FROM_ORDER_BY:
            case DZ_STATE_FROM_INITIAL:
                soDebug("dzManager: DZ_STATE_FROM...", SOD_LO);
                $SesData->State = DZ_STATE_HANDLE_EVENT;
                break;

            case DZ_STATE_FROM_EDIT_LOOP:
            case DZ_STATE_FROM_NEW_LOOP:
                soDebug("dzManager: DZ_STATE_FROM_EDIT/NEW_LOOP", SOD_LO);
                $SesData->State = dzSMH_FromEditLoop($SesData);
                break;

            case DZ_STATE_FROM_QUERY_LOOP:
                soDebug("dzManager: DZ_STATE_FROM_QUERY_LOOP", SOD_LO);
                $SesData->State = dzSMH_FromQueryLoop($SesData);
                break;

            case DZ_STATE_FROM_SUB_VIEW:
                soDebug("dzManager: DZ_STATE_FROM_SUB_VIEW", SOD_LO);
                $SesData->State = dzSMH_FromSubView($SesData);
                break;

            case DZ_STATE_GO_BROWSE:
                soDebug("dzManager: DZ_STATE_GO_BROWSE", SOD_LO);
                $SesData->State = dzSMH_GoBrowse($SesData);
                break;

            case DZ_STATE_GO_DIALOG:
                soDebug("dzManager: DZ_STATE_GO_DIALOG", SOD_LO);
                $SesData->State = dzSMH_GoDialog($SesData);
                break;

            case DZ_STATE_GO_EDIT_LOOP:
                soDebug("dzManager: DZ_STATE_GO_EDIT_LOOP", SOD_LO);
                $SesData->State = dzSMH_GoEditLoop($SesData);
                break;

            case DZ_STATE_GO_INITIAL:
                soDebug("dzManager: DZ_STATE_GO_INITIAL", SOD_LO);
                $SesData->State = dzSMH_InitialView($SesData);
                break;

            case DZ_STATE_GO_LOOK_UP:
                break;

            case DZ_STATE_GO_ORDER_BY:
                break;

            case DZ_STATE_GO_QUERY_LOOP:
                soDebug("dzManager: DZ_STATE_GO_QUERY_LOOP", SOD_LO);
                $SesData->State = dzSMH_GoQueryLoop($SesData);
                break;

            case DZ_STATE_HACKER_IN:
                soDebug("dzManager: DZ_STATE_HACKER_IN", SOD_LO);
                $SesData->State = dzSMH_HackerIn($SesData);
                break;

            case DZ_STATE_HANDLE_EVENT:
                soDebug("dzManager: DZ_STATE_HANDLE_EVENT", SOD_LO);
                $SesData->State = dzSMH_HandleEvent($SesData);
                break;

            case DZ_STATE_INITIAL:
                soDebug("dzManager: DZ_STATE_INITIAL", SOD_LO);
                $SesData->State = dzSMH_Initial($SesData);
                break;

            case DZ_STATE_INIT_BROWSE:
                soDebug("dzManager: DZ_STATE_INIT_BROWSE", SOD_LO);
                $SesData->State = dzSMH_InitBrowse($SesData);
                break;

            case DZ_STATE_INSERT_LINE:
                soDebug("dzManager: DZ_STATE_INSERT_LINE", SOD_LO);
                $SesData->State = dzSMH_InsertLine($SesData);
                break;

            case DZ_STATE_INSERT_FORM:
                soDebug("dzManager: DZ_STATE_INSERT_FORM", SOD_LO);
                $SesData->State = dzSMH_InsertForm($SesData);
                break;

            case DZ_STATE_JUMP_TO_BROWSE_POS:
                soDebug("dzManager: DZ_STATE_JUMP_TO_BROWSE_POS", SOD_LO);
                $SesData->State = dzSMH_jumptobrowsepos($SesData);
                break;

            case DZ_STATE_KILL_VIEW:
                soDebug("dzManager: DZ_STATE_KILL_VIEW", SOD_LO);
                $SesData->State = dzSMH_KillView($SesData);
                break;

            case DZ_STATE_PRODUCE_OUTPUT:
                soDebug("dzManager: DZ_STATE_PRODUCE_OUTPUT", SOD_LO);
                $result = $SesData->buildForm();
                if ($result)
                    $SesData->State = DZ_STATE_FINISH;
                else
                    $SesData->State = DZ_STATE_GO_INITIAL;
                break;

            case DZ_STATE_RECOVER_ERROR:
                soDebug("dzManager: DZ_STATE_RECOVER_ERROR", SOD_LO);
                $SesData->State = dzSMH_RecoverError($SesData);
                break;

            case DZ_STATE_RETURN_QUERYING:
                soDebug("dzManager: DZ_STATE_RETURN_QUERYING", SOD_LO);
                $SesData->State = dzSMH_ReturnQuerying($SesData);
                break;

            case DZ_STATE_SHOW_HELP:
                soDebug("dzManager: DZ_STATE_SHOW_HELP", SOD_LO);
                $SesData->State = dzSMH_ShowHelp($SesData);
                break;

            case DZ_STATE_START_EDIT:
                soDebug("dzManager: DZ_STATE_START_EDIT", SOD_LO);
                $lockinfo = dzSMH_StartEdit($SesData);
                if (!$lockinfo) {
                    $SesData->State = DZ_STATE_GO_BROWSE;
                } else {
                    $SesData->State = DZ_STATE_GO_EDIT_LOOP;
                };
                break;

            case DZ_STATE_START_NEW:
                soDebug("dzManager: DZ_STATE_START_NEW", SOD_LO);
                $SesData->State = dzSMH_StartNew($SesData);
                break;

            case DZ_STATE_START_PICK_UP:
                soDebug("dzManager: DZ_STATE_START_PICK_UP", SOD_LO);
                $SesData->State = dzSMH_StartPickUp($SesData);
                break;

            case DZ_STATE_START_QUERY:
                soDebug("dzManager: DZ_STATE_START_QUERY", SOD_LO);
                $SesData->State = dzSMH_StartQuery(&$SesData);
                break;

            case DZ_STATE_SUSPEND:
                soDebug("dzManager: DZ_STATE_SUSPEND", SOD_LO);
                $SesData->State = dzSMH_Suspend($SesData);
                break;

            case DZ_STATE_TERMINATE_SESSION:
                soDebug("dzManager: DZ_STATE_TERMINATE_SESSION", SOD_LO);
                /* Session was finished */
                $SesData->State = dzSMH_TerminateSes($SesData);
                break;

            case DZ_STATE_TO_PARENT_VIEW:
                soDebug("dzManager: DZ_STATE_TO_PARENT_VIEW", SOD_LO);
                $SesData->State = dzSMH_ToParentView($SesData);
                break;

            case DZ_STATE_UNDEFINED:
                soDebug("dzManager: DZ_STATE_UNDEFINED", SOD_LO);
                $SesData->State = dzSMH_UndefinedState($SesData);
                break;

            case DZ_STATE_UPD_FETCH_FIELD:
                soDebug("dzManager: DZ_STATE_UPD_FETCH_FIELD", SOD_LO);
                $SesData->State = dzSMH_UpdFetchField($SesData);
                break;

            case DZ_STATE_VALIDATE_FIELD:
                soDebug("dzManager: DZ_STATE_VALIDATE_FIELD", SOD_LO);
                $SesData->State = dzSMH_ValidateField($SesData);
                break;

            default:
                /* This is an error state. Execution should never come here */
                soDebug("dzManager: Unknown state for Dataz state machine", SOD_LO_E);
                /* Set finishing parameters */
                $SesData->HotView->Template->RequestResult->NextState = 0;
                $SesData->HotView->Template->RequestResult->Screen = "";
                $SesData->HotView->Template->RequestResult->Status = DZ_MANAGER_ERROR;
                $SesData->HotView->Template->RequestResult->Serialize = DZMNG_SER_DEL;
                $SesData->State = DZ_STATE_FINISH;
        } // switch (SesData->$State)
    } // for (;;)
} // dzRunStateMachine


/** Marks session to be under processing
 * Asema has list of session it is currently processing. Add given session
 * to the list of processed sessions. No session can be twice under processing.
 *
 * >$SessionId:str  Id of the session to add
 * <:bool true if succesfull. False is session was already on the list of
 *        processed sessions.
 */
function addToProcessedList($SessionId){
    $file_name = "file://".dzGetIniPath(DZID_CACHE_FOLDER).
                 "/".DZ_PROCESS_LIST_FILE;

    $instream = new soInputStream($file_name);
    if (!$instream->Error) {
        $process_list = unserialize($instream->Data);
        /* Check if the process is under process already */
        if ($process_list->itemExists($SessionId)) {
            $result = false;
        } else {
            $process_list->setItem($SessionId, 1);
            $result = true;
        } // if ($process_list->ItemExist($SessionId))
    } else {
        $process_list = new soSet();
        $process_list->setItem($SessionId, 1);
        $result = true;
    } // if (!$instream->Error)

    $Stream = new soOutputStream($file_name, serialize($process_list));

    return $result;
} // addToProcessedList


/** Take the session away from the list of processed sessions
 *
 * >$SessionId:str  Id of the session to add
 * <:bool true if succesfull otherwise false
 */
function delFromProcessedList($SessionId){
    $file_name = "file://".dzGetIniPath(DZID_CACHE_FOLDER).
                 "/".DZ_PROCESS_LIST_FILE;

    $instream = new soInputStream($file_name);
    if (!$instream->Error) {
        $process_list = unserialize($instream->Data);
        /* Check that the process is marked under process */
        if ($process_list->itemExists($SessionId)) {
            $process_list->delItem($SessionId);
            $result = true;
        } else {
            $result = false;
        } // if ($process_list->ItemExist($SessionId))
    } else {
        $process_list = new soSet();
        $result = false;
    } // if (!$instream->Error)

    $Stream = new soOutputStream($file_name, serialize($process_list));

    return $result;
} // delFromProcessedList


/** Create new session
 *  Create new session and fill in the basic user data
 *  @param string $SesId the id for the new session
 *  @param string $UserId the user name of the owner
 *  @param string >$Password The user's password
 *  @return dzSession <new session
 */
function dzManNewSession($SesId, $UserId, $Password, $Language) {
    soDebug("dzManager: Creating a new session with id $SesId", SOD_DET);
    /* Create the class that will contain the session information. */
    $new_session = new dzSession($SesId);
    /* Find out the user level and language. Send the information request to
       Kone. First create request Itu */
    $itu = new dzItu();

    /* set request type & user id */
    $itu->setBlockItem(DZI_CONTROL, DZK_REQUEST, DZK_REQ_USER_INFO);
    $itu->setBlockItem(DZI_CONTROL, DZK_USER, $UserId);

    /* Send result to kone */
    $result_itu = sendRpcToKone($itu);
    $result = $result_itu->getBlockItemAt(DZI_CONTROL, DZK_RESULT);
    /* check for rpc error */
    if ((isRpcError($result_itu)) || ($result != DZR_OK)) {
        $fault_string = getRpcErrorText($result_itu);
        $new_session->State = DZ_STATE_HACKER_IN;
    } // if (isRpcError($result_itu))

    /* Fill in the basic information. */
    $new_session->BasicInfo->UserID = $UserId;
    $new_session->BasicInfo->Groups =
        soExplode($result_itu->getBlockItemAt(DZI_CONTROL, DZU_GROUPS), ";");
    $new_session->BasicInfo->UserName =
        $result_itu->getBlockItemAt(DZI_CONTROL, DZU_FULLNAME);
    $new_session->BasicInfo->UserLevel =
        $result_itu->getBlockItemAt(DZI_CONTROL, DZU_LEVEL);

    /* Sort out the language code. The prefred lang is:
     * a) user, b) from login prompt or c) app default */
    $lang = $result_itu->getBlockItemAt(DZI_CONTROL, DZU_LANG);
    if  (!$lang) $lang = $Language;
    if  (!$lang) $lang = dzGetIniItem("general", "lang", "app");

    $new_session->BasicInfo->Lang = $lang;
    $new_session->State = DZ_STATE_GO_INITIAL;

    return $new_session;
} // dzManNewSession


/** Produce a message that is shown to the user.
 */
function dzGetManagerMsg($MessageId) {
    switch ($MessageId) {
        case DZ_STATE_HACKER_IN:
            $Message = "You bad hacker! Go away. Nalle does not like you!";
            break;
        default:
        $Message = "Error: No message defined for this state";
    }
    $result = "<HTML><BODY><CENTER><H4>\n";
    $result .= "$Message";
    $result .= "</H4></CENTER></BODY></HTML>\n";

    return $result;
} // dzGetManagerMsg
?>
